بنية التكرارات


تؤفر البيرل عدة من الانواع لبنى التكرارية وهي مفيدة لتكرر كود ما في البرنامج ويستعمل باستمرار في البرنامج



While

تسمى بالبنية التكرارية ذات الشرط المسبق

الصيغة :

while ( CONDITION ) {
statement }

اذا كان الشرط true يتم تنفيذ التعليمات في البلوك وبعد التنفيذ يتم اختبار الشرط مرة اخرى .... وهكذا حتي يصبح الشرط false فعلى سبيل المثال

#!/usr/bin/perl -w
$i = 1;
while($i <= 5) {
print "While:" . $i . "\n";
$i++;
}
print "outside:$i\n";
#!/usr/bin/perl -w
$x = <STDIN>; 
chomp $x; 
$b = <STDIN>; 
chomp $b; 
$q=int(x/b);
$r=$x-$b*$q;
if(r==0) print " the A IS DIVISIBLE BY B \n";

برنامج يدخل نسق من الأرقام ثم يقوم بعكس النسق بحيث يصبح أخر عدد فيه هو الأول والثاني هوماقبل الأخير مثلاً النسق 123 يحول إلى النسق 321

#!/usr/bin/perl -w
$x = <STDIN>; 
chomp $x; 
$i = $x;
while($i>=1) 
{
print $i%10;
$i=int($i/10);
}

إستخدام الدالة int لتحويل من الكسور العشرية الى الصحيحة فهي تزيل جزء الكسور ويعاد جزء الصحيح

خوارزمية القسمة THE DIVISION ALGORITHM
نظرية : إذا كان لدينا عددين صحيح y,b ، وكان b اكبر من صفر ، اذا سيكون لدينا عددين q,r بحيث :

y=b*q+r   

q هو حاصل القسمة Quotient . 
r هو باقي القسمة remainder .
b هو المقسوم Divisor .
y هو القاسم  divident .
حيث باقي القسمة r اكبر او يساوي 0 وأقل من العدد b

مثال :
-21=5*q+r

بقسمة -21 على 5 ، سوف نحصل على حاصل القسمة -4 ، والباقي سوف يكون -1 ، ولكن كما ذكرنا في القاعدة السابقة أن الباقي r دائماً يكون موجب ، لذلك نقوم بجمع 5 في الباقي ، ونطرح 1 من حاصل القسمة لذا يكون لدينا q=-5 ، r=4
نطبقها في المعادلة :
4+-5*5 =-21 وهو صحيح .
احد الخوازميات المهمة جداً خوارمية القسمة  حيث حاصل القسمة الاولة هو 12 وتم حساب باقي القسمة بواسطة القانون  
الفكره استخدام خوارزمية القسمة y=b*q+r حيث الاساس  b=10 

123/10=12		3
12/10=1		2
1/10=0		1



q1=q0/b		r1=q0-b*q1
q2=q1/b		r2=q1-b*q2
q3=q2/b 		r3=q2-b*q3
qi+1=qi/b		ri+1=qi-b*qi+1


do...while

ذات الشرط الملحق

الصيغة :

do {

    STATEMENTS

} while (CONDITION);

تنفذ التعليمة STATEMENTS ثم يتم اختبار الشرط فإذا كانت نتيجة الاختبار true يعاد تنفيذ التعليمة STATEMENTS قعلى سبيل المثال

#!/usr/bin/perl -w
$i = 1;
do
{
print "While:" . $i . "\n";
$i++;
}
while($i <= 5);
print "outside:$i\n";


Until

ذات الشرط المسبق

الصيغة :

until ( CONDITION ) {
statement }

عكس while اذا كان الشرط false يتم تنفيذ التعليمات في البلوك ،ثم بعد التنفيذ يتم اختبار الشرط مرة اخرى .... وهكذا حتي يصبح الشرط true

#!/usr/bin/perl -w
$i = 1;
until($i <= 5) {
print "until:" . $i . "\n";
$i++;
}
print "outside:$i\n";

فهو لايدخل الى بلوك التكرار



do...until

ذات الشرط الملحق

الصيغة :

do {

    STATEMENTS

} until (CONDITION);

على سبيل المثال

#!/usr/bin/perl -w
$i = 1;
do
{
print "until:" . $i . "\n";
$i++;
}
until($i <= 5);
print "outside:$i\n";


For

الصيغة :

for (initialization; CONDITION ; update) {
statement  }

initialization القيمة الابتدايئة ، Condition شرط الاستمرار ، update القيمة الجديدة .

حيث تأخذ قيمة متغير الحلقة في القيمة الابتدايئة ويتم اختبار شرط الاستمرار فاذا كان الشرط محقق يتم الدخول الى الحلقة ثم يعطى قيمة المتغير قيمة جديدة ويعاد اختبار الشرط ....وهكذا .

#!/usr/bin/perl -w
for($i = 1; $i <= 5; $i++)
{
  print "For:" . $i . "\n";
}
print "outside:$i\n";

بفرض أننا نرغب بحساب الجذر بإستخدام حلقة التكرار for

#!/usr/bin/perl -w
$x = <STDIN>; 
chomp $x; 
for($i = 1; $i <= $x; $i++)
{
  $y=$x/$i;
  if($y*$y== $x) print "square root is:" . $y . "\n";
}

نبدأ i=1 ثم وضع حاصل قسمة x مع i في y ونستمر في زيادة i حتى يكون مربع y يساوي المدخل لذلك هو الجذر التربيعي لx .



Foreach

هي من التكرارات المفيده لتسهيل العمل مع القوائم وذلك للمرور خلال المصفوفة او المصفوفة المترابطة الصيغة :

foreach LOOP_VAR (ARRAY) {

    STATEMENTS

}

على سبيل المثال المؤثر .. يتضمن قائمة من الاعداد 1 و 2 ....الى 5

#!/usr/bin/perl –w
$n = 1;
foreach $n (1..5) {
print "Foreach:" . $n . "\n";
$n++;
}

للمرورعبر عناصر المصفوفة

#!/usr/bin/perl -w
@array = ("one", "two", "three");
foreach $iterator (@array) {
print "The value of the iterator is now $iterator \n";
}

بنفس المثال مع المتغير الخاص $_ هو الافتراضي في كثير من الحالات

@array = ("one", "two", "three", "four");
for (@array) {
print "The value of the iterator is now $_ \n";
}

نلاحظ استعمال for في الصيغة بدلاً من foreach ، ينفذ مفسر البيرل التعليمات كما لوكانت foreach ،عن طريق استخدام التكررات الاخرى لعرض المصفوفة بإستخدام for

@array = ("one", "two", "three", "four");
for($i=0;$i<scalar(@array);$i++) {
print "The value of the iterator is now $array[$i]\n";
}

والمصفوفات المترابطة يمكن ان نصل الى كل عناصرها باستعمال الدوال keys, values ،فالدالة keys تعيد الفهارس ودالة values تعيد القيمة .

%ages = ("Michael Caine", 39,"Dirty Den", 34,"Angie", 27,
         "Willy", "21 in dog years","The Queen Mother", 108);
foreach $person (keys %ages)
{
        print "I know the age of $person\n";
}
foreach $age (values %ages)
{
        print "Somebody is $age\n";
}

للمرور عبر المصفوفة المترابطة من خلال الفهرس والقيمة في وقتاُ واحد

%ages = ("Michael Caine", 39,"Dirty Den", 34,"Angie", 27,
         "Willy", "21 in dog years","The Queen Mother", 108);
foreach $person (keys %ages)
{
        print "I know the age of $person is $ages{$person}\n";
}

او للمرور عبر المصفوفة المترابطة من خلال الفهرس والقيمة في وقتاً واحد من خلال الدالة each حيث تقوم باعادة الفهرس والقيمة الذي يلية تستعمل مع while

#!/usr/bin/perl
%array = ( "100", "Green", "200", "Orange");
while (($key, $value) = each(%array)) {
      print("$key = $value\n");
}


ملاحظة :

الاسهل او الاقل شيوعاً طريقة التكرارات عبر الهاش بأستعمال الدالة keys لإعادة قائمة المفاتيح لكن هذة القائمة في الحقيقة تقوم بعمل نسخ من الهاش لذلك لانستطيع تعديل أسماء المفاتيح

#!/usr/bin/perl
# iterate.pl
use strict;
use warnings;
my %hash = ('Key1' => 'Value1', 'Key2' => 'Value2');
# dump of hash
print "$_ => $hash{$_} \n" foreach keys %hash;

ايضاً اذا اردت فرز قائمة المفاتيح بإستخدام الدالة sort

# sorted dump of hash
print "$_ => $hash{$_} \n" foreach sort keys %hash;

هذا بإلنسبة عندما لانهتم بأمر المفاتيح كذلك لايختلف الحال مع القيم بواسطة الدالة values

# print list of sorted values
foreach (sort values %hash) {
print "Value: $_ \n";
}

فهي تعيد قائمة القيم التي تنسخ من الهاش ولذلك لايمكن تعديل القيم الاصلية في هذة الطريقة ،اذا كانت تود تعدل القيم الاصلية يمكن عمل ذلك بواسطة هذا التكرار :

# increment all hash values by one
foreach (@hash{keys %hash}) {
$_++;
}

في هذا المثال يستخدم مايسمى بالـaliasing بحيث يصبح وسيط المتغير الخاص الافتراضي $_ مباشرتاً أسماًَ مستعار له بدلاً من نسخ القيمة التي تشير اليه.


مازالت ايضاً توجد معضلة عند فرز القيم

# print list of sorted values
foreach (sort values %hash) {
print "Value: $_ \n";
}

بحيث لانستطيع في هذة الطريقة العودة بسهولة الى المفاتيح ، أن رغبت بالوصول الى القيم المرتبة من خلال keys نقوم بالتالي:

# sort a hash by values
foreach (sort { $hash{$a} cmp $hash{$b} } keys %hash) {
print "$hash{$_} <= $_ \n";
}

معرفة هذا الشي مهم للتغير القيم في الهاش


ملاحظة اخرى ،المعالجة مع الكلمة المحجوزة foreach تقوم بإنتزاع كل المفاتيح (او القيم ) مرة واحدة فقط ثم التكرار يعمل من خلالهم ،هذا العمل غير كفوء في الذاكرة خصوصاً اذا كان الهاش كبير ، وبالطبع كلما كان الهاش كبير كلما ازدات كلفة فحصه واستخراج الموارد المطلوبة ، لذلك نستطيع العمل مع إجراء بديل من خلال الدالة each التي تعيد المفتاح والقيمة الذي يلية المستخدمة مع while التكرارية

while (($key, $value) = each %hash) {
print "$key => $value \n";
$hash{$key}++;
}

الطلب بواسطة الدالة each لإنجاز المفتاح والقيمة تعمل بنفس الدوال keys و values إي لايمكن الوصول المباشر الى الهاش .



الكلمات المحجوزة للقفز

تؤفر البيرل بعض الكلمات المحجوزة لتغير تدفق البرنامج و تستعمل لسيطرة على التكرار


الكلمة المحجوزة الوصف
last القفز الى خارج البلوك الحالي
next القفز الى البلوك التالي مع اختبار الشرط مرة اخرى
redo اعد تشغيل البلوك الحالي بدون اختبار الشرط
goto القفز الى label المحدد


المثال التالي عدة أشكال للتكرارت

#!/usr/bin/perl
use warnings;
use strict;
my $n = 0;

print "With a while loop:\n";
while (++$n < 4) {print "Hello $n \n";}

print "With a foreach loop:\n";
foreach my $n (1..3) { print "Hello $n \n"; }

print "With a bare block and redo: \n";
$n = 1; { print "Hello $n \n";
last if (++$n > 3); redo; }

استخدامنا بلوك مجرد bare block في المثال السابق والبنية الشريطة كاالتكرارت ، يحدث التالي في الشرطية if اذا تحقق الشرط فسوف يخرج من البلوك باستعمال last ، واذا لم يتحقق الشرط فسوف يعاد البلوك عن طريق استعمال redo


Output

With a while loop:
Hello 1
Hello 2
Hello 3
With a foreach loop:
Hello 1
Hello 2
Hello 3
With a bare block and redo:
Hello 1
Hello 2
Hello 3


الكلمة المحجوزة next تستعمل لمهام الضروية في التكرارات لتجاهل الكود المتبقي في البلوك المثال التالي يوضح هذا

#!/usr/bin/perl
# config.pl
use warnings;
use strict;
my %config = ();
while (<>) {
chomp;   #ازلة السطر الجديد من المتغير الخاص  _$ الافتراضي
next if /^\s*$/; #  عند احتوى whitespace سيعيد اختبار التكرار 
last if /^\s*\#/; #سوف يخرج  عند احتوى رمز التعليق

my ($param, $value) = split("=", $_, 2);   #split on first '='
print $_,"\n"; 
unless ($value) {
print ("No value for parameter '$_' \n");
next;   # يعيد اختبار التكرار
}
$config{$param} = $value;
}
foreach (sort keys %config) {
print "$_ => $config{$_} \n";
}

الكلمة المحجوزة next هي في لغة السي والجافا continue التي تتجاهل الكود المتبقي في بلوك التكرار ، بينما في لغة البيرل عند تضمين بلوك continue في التكرار الحالي فسوف تنفذه وهنا برنامج يوضح هذا

my $n = 0;
while ($n < 10) {
next unless($n % 2==0);
print "1: $n \n";
} continue {
# 'next' هنا
$n++;
#print "2: $n \n";
}
# 'last' هنا 

بامكان اضافة بلوك continue لكل التكرارات التي تصبح جزء من كود التكرار الذي ينفذ بعد تنفيذ جسم التكرار ، على خلاف عندما يحوي بلوك التكرارعلى الكلمة المحجوزة next في هذة الحالة فأن بلوك continue سوف ينفذ بعد اعاءدته الى التكرار.

تعرضنا سابقاً لجملة last , next , redo الكلمة المحجوزة الاخر للقفز goto ، لإستخدام goto أولاً يلزم تحديد label معنون في البرنامج حتى نستطيع القفز اليه نسجل الاسم متبوعاً بنقطتين

my $n = 0;
while ($n < 10) {
goto CONTINUE,unless($n % 2==0);
print $n,"\n";
CONTINUE:
$n++;
}

الطريقة السابقة ليست محببه لكن الافضل استخدام next .

بإنشاء label معنون يحق أستخدم الكلمات والارقام لإسماء label بشرط ان لايكون الاسم عبارة عن كلمة محجوزه للغه البيرل

#!/usr/bin/perl
# blockmain.pl
use strict;
use warnings;
# المتغيرات العامة
my $global_variable = "All the World can see Me";
use constant MY_GLOBAL_CONSTANT => "Global Constant";
# هنا كود الرئيسي للبرنامج
MAIN: {
# في داخل البلوك المتغيرات تعرف محلية وليست عامة
my $main_variable = "Not visible outside main block";
print_variables ($main_variable);
}

sub print_variables {
print $global_variable, "\n", MY_GLOBAL_CONSTANT, "\n";
# print $main_variable, "\n"; #خطأ 
print $_[0], "\n"; # صحيح 
}

يبدا البرنامج تعريف المتغيرات العامة والثوابت ،ثم ينفذ المفسر كود MAIN بخلاف الدالة التي لاتنفذ الا بإستدعى ، label في السي الصداقة friendlier ، مما لاشك فية يمكن انشاء دالة main في البيرل كماهي في السي .